/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
 */
/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <hilog/log.h>
#include <iostream>
#include <memory>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <multimedia/player_framework/native_avbuffer.h>
#include "AVCodecSampleError.h"
#include "Recorder.h"

#undef LOG_TAG
#define LOG_TAG "AudioRecorder"

namespace {
using namespace std::chrono_literals;
constexpr int64_t MICROSECOND = 1000000;
constexpr int64_t ONE_THOUSAND = 1000;
constexpr int RECORDER_BURDEN_ONE = -1;
constexpr int ZERO = 0;
constexpr int TWO = 2;
constexpr int THREE = 3;
constexpr int ONE_ZERO_TWO_FORE = 1024;
} // namespace

Recorder::~Recorder()
{
    StartRelease();
    if (releaseThread_ && releaseThread_->joinable()) {
        releaseThread_->join();
    }
}

int32_t Recorder::Init(SampleInfo &sampleInfo)
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (isStarted_ || muxer_ != nullptr || videoEncoder_ != nullptr) {
        StartRelease();
        WaitForDone();
    }
    isStarted_ = false;
    isExiting_ = false;
    isReleasing_ = false;
    isFirstFrame_ = true;
    sampleInfo_ = sampleInfo;
    audioEncoder_ = std::make_unique<AudioEncoder>();
    videoEncoder_ = std::make_unique<VideoEncoder>();
    muxer_ = std::make_unique<Muxer>();
    int32_t ret = videoEncoder_->Create(sampleInfo_.videoCodecMime);
    if (ret != AVCODEC_SAMPLE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "Create video encoder failed");
        StartRelease();
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    ret = muxer_->Create(sampleInfo_.outputFd);
    if (ret != AVCODEC_SAMPLE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "Create muxer with fd(%{public}d) failed",
                     sampleInfo_.outputFd);
        StartRelease();
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    ret = muxer_->Config(sampleInfo_);
    if (ret != AVCODEC_SAMPLE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "Muxer config failed");
        StartRelease();
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    ret = CreateAudioEncoder();
    if (ret != AVCODEC_SAMPLE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "Create audio encoder failed");
        StartRelease();
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    ret = CreateVideoEncoder();
    if (ret != AVCODEC_SAMPLE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "Create video encoder failed");
        StartRelease();
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    sampleInfo.window = sampleInfo_.window;
    isFirstFrame_ = true;
    OH_LOG_INFO(LOG_APP, "Initialize successful");
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t Recorder::Start()
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (isStarted_) {
        OH_LOG_ERROR(LOG_APP, "Already started.");
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    if (encContext_ == nullptr) {
        OH_LOG_ERROR(LOG_APP, "Already started.");
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    if (videoEncoder_ == nullptr || muxer_ == nullptr) {
        OH_LOG_ERROR(LOG_APP, "Already started.");
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    int32_t ret = muxer_->Start();
    if (ret != AVCODEC_SAMPLE_ERR_OK) return AVCODEC_SAMPLE_ERR_ERROR;
    if (encContext_) {
        ret = videoEncoder_->Start();
        if (ret != AVCODEC_SAMPLE_ERR_OK) return AVCODEC_SAMPLE_ERR_ERROR;
        isStarted_ = true;
        encContext_->inputBufferInfoQueue.size();
        videoEncInputThread_ = std::make_unique<std::thread>(&Recorder::VideoEncInputThread, this);
        encOutputThread_ = std::make_unique<std::thread>(&Recorder::EncOutputThread, this);
        if (encOutputThread_ == nullptr || videoEncInputThread_ == nullptr) {
            StartRelease();
            return AVCODEC_SAMPLE_ERR_ERROR;
        }
        if (encContext_ != nullptr) {
            encContext_->ClearCache();
        }
    }
    if (audioEncContext_) {
        ret = audioEncoder_->Start();
        if (ret != AVCODEC_SAMPLE_ERR_OK) return AVCODEC_SAMPLE_ERR_ERROR;
        isStarted_ = true;
        audioEncContext_->inputBufferInfoQueue.empty();
        audioEncInputThread_ = std::make_unique<std::thread>(&Recorder::AudioEncInputThread, this);
        audioEncOutputThread_ = std::make_unique<std::thread>(&Recorder::AudioEncOutputThread, this);
        if (audioEncInputThread_ == nullptr || audioEncOutputThread_ == nullptr) {
            StartRelease();
            return AVCODEC_SAMPLE_ERR_ERROR;
        }
        if (audioEncContext_ != nullptr) {
            audioEncContext_->ClearCache();
        }
    }
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t Recorder::PushAudioData(const uint8_t* data, size_t size, int64_t timestamp)
{
    if (!isStarted_) {
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    const size_t MAX_QUEUE_SIZE = ONE_THOUSAND;
    std::lock_guard<std::mutex> lock(audioMutex_);
    if (audioQueue_.size() >= MAX_QUEUE_SIZE) {
        OH_LOG_WARN(LOG_APP, "Audio queue full, dropping frame");
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    AudioFrame frame;
    frame.data.assign(data, data + size);
    frame.timestamp = timestamp;
    audioQueue_.push(std::move(frame));
    audioCond_.notify_one();
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t Recorder::PushVideoData(const uint8_t* data, size_t size, int64_t timestamp, bool isKeyFrame)
{
    if (!isStarted_) {
        OH_LOG_WARN(LOG_APP, "Recorder not started, ignoring video data");
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    OH_LOG_INFO(LOG_APP, "PushVideoData: data=%{public}p, size=%{public}zu,"
                         "timestamp=%{public}ld, isKeyFrame=%{public}d",
                data, size, timestamp, isKeyFrame);
    if (data == nullptr || size == ZERO) {
        OH_LOG_ERROR(LOG_APP, "Invalid video data parameters");
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    
    VideoFrame frame;
    frame.data.assign(data, data + size);
    frame.timestamp = timestamp;
    frame.isKeyFrame = isKeyFrame;

    const size_t MAX_VIDEO_QUEUE_SIZE = ONE_ZERO_TWO_FORE*ONE_ZERO_TWO_FORE;
    std::unique_lock<std::mutex> lock(videoMutex_);
    size_t actualMaxSize = isKeyFrame ? MAX_VIDEO_QUEUE_SIZE * TWO : MAX_VIDEO_QUEUE_SIZE;
    
    if (videoQueue_.size() >= actualMaxSize) {
        if (!isKeyFrame) {
            OH_LOG_WARN(LOG_APP, "Video queue full, dropping non-key frame");
            return AVCODEC_SAMPLE_ERR_ERROR;
        }
        
        bool dropped = false;
        std::queue<VideoFrame> tempQueue;
        while (!videoQueue_.empty()) {
            VideoFrame frame = videoQueue_.front();
            videoQueue_.pop();
            if (!frame.isKeyFrame && !dropped) {
                OH_LOG_WARN(LOG_APP, "Dropping non-key frame to make space for key frame");
                dropped = true;
                continue;
            }
            tempQueue.push(std::move(frame));
        }
        videoQueue_ = std::move(tempQueue);
        if (videoQueue_.size() >= actualMaxSize) {
            OH_LOG_WARN(LOG_APP, "Video queue still full after cleanup, dropping oldest frame");
            videoQueue_.pop();
        }
    }
    videoQueue_.push(std::move(frame));
    OH_LOG_DEBUG(LOG_APP, "After push, queue size: %{public}zu", videoQueue_.size());
    videoCond_.notify_one();
    return AVCODEC_SAMPLE_ERR_OK;
}

void Recorder::VideoEncInputThread()
{
    while (isStarted_ && !isExiting_) {
        VideoFrame frame;
        {
            std::unique_lock<std::mutex> lock(videoMutex_);
            videoCond_.wait_for(lock, 5s, [this]() {
                return !isStarted_ || isExiting_ || !videoQueue_.empty();
            });
            if (!isStarted_ || isExiting_) break;
            if (videoQueue_.empty()) continue;
            frame = std::move(videoQueue_.front());
            videoQueue_.pop();
        }
        std::unique_lock<std::mutex> lock(encContext_->inputMutex);
        encContext_->inputCond.wait_for(lock, 5s, [this]() {
            return !isStarted_ || isExiting_ || !encContext_->inputBufferInfoQueue.empty();
        });
        if (!isStarted_ || isExiting_) break;
         if (!encContext_) break;
        if (encContext_->inputBufferInfoQueue.empty()) continue;
        CodecBufferInfo bufferInfo = encContext_->inputBufferInfoQueue.front();
        encContext_->inputBufferInfoQueue.pop();
        encContext_->inputFrameCount++;
        if (!bufferInfo.buffer) continue;
        OH_AVBuffer* avBuffer = reinterpret_cast<OH_AVBuffer*>(bufferInfo.buffer);
        size_t bufferCapacity = OH_AVBuffer_GetCapacity(avBuffer);
        if (frame.data.size() > bufferCapacity) {
            encContext_->inputBufferInfoQueue.push(bufferInfo);
            continue;
        }
        uint8_t *inputBufferAddr = OH_AVBuffer_GetAddr(avBuffer);
        if (!inputBufferAddr) {
            encContext_->inputBufferInfoQueue.push(bufferInfo);
            continue;
        }
        if (!CopyFrameToBuffer(frame, bufferInfo, inputBufferAddr, bufferCapacity)) {
            encContext_->inputBufferInfoQueue.push(bufferInfo);
            continue;
        }
        lock.unlock();
        int32_t ret = videoEncoder_->PushInputData(bufferInfo);
        if (ret != AVCODEC_SAMPLE_ERR_OK) break;
    }
    OH_LOG_INFO(LOG_APP, "Video encoder input thread exiting");
}

bool Recorder::CopyFrameToBuffer(const VideoFrame& frame, CodecBufferInfo& bufferInfo,
                                 uint8_t* inputBufferAddr, size_t bufferCapacity)
{
    int32_t widthStride = encContext_->widthStride;
    int32_t heightStride = encContext_->heightStride;
    OH_LOG_ERROR(LOG_APP, "encContext_->widthStride = %{public}d, encContext_->heightStride = %{public}d",
                 encContext_->widthStride, encContext_->heightStride);
    
    if (widthStride == 0) widthStride = sampleInfo_.videoWidth;
    if (heightStride == 0) heightStride = sampleInfo_.videoHeight;
    
    size_t requiredCapacity = widthStride * heightStride * THREE / TWO;
    if (requiredCapacity > bufferCapacity) {
        OH_LOG_ERROR(LOG_APP, "Required buffer capacity (%zu) exceeds buffer capacity (%zu), dropping frame",
                     requiredCapacity, bufferCapacity);
        return false;
    }
    uint8_t *yDest = inputBufferAddr;
    const uint8_t *ySrc = frame.data.data();
    for (int32_t i = ZERO; i < sampleInfo_.videoHeight; i++) {
        std::copy(ySrc, ySrc + sampleInfo_.videoWidth, yDest);
        yDest += widthStride;
        ySrc += sampleInfo_.videoWidth;
    }
    uint8_t *uvDest = inputBufferAddr + widthStride * heightStride;
    const uint8_t *uvSrc = frame.data.data() + sampleInfo_.videoWidth * sampleInfo_.videoHeight;
    for (int32_t i = ZERO; i < sampleInfo_.videoHeight / TWO; i++) {
        std::copy(uvSrc, uvSrc + sampleInfo_.videoWidth, uvDest);
        uvDest += widthStride;
        uvSrc += sampleInfo_.videoWidth;
    }
    bufferInfo.attr.size = ZERO;
    bufferInfo.attr.pts = frame.timestamp;
    bufferInfo.attr.flags = frame.isKeyFrame ? AVCODEC_BUFFER_FLAGS_SYNC_FRAME : AVCODEC_BUFFER_FLAGS_NONE;
    return true;
}

void Recorder::EncOutputThread()
{
    while (true) {
        if (!isStarted_) break;
        std::unique_lock<std::mutex> lock(encContext_->outputMutex);
        encContext_->outputCond.wait_for(lock, 2s, [this]() {
            return !isStarted_ || !encContext_->outputBufferInfoQueue.empty();
        });
        if (!isStarted_) break;
        
        if (encContext_->outputBufferInfoQueue.empty()) continue;
        CodecBufferInfo bufferInfo = encContext_->outputBufferInfoQueue.front();
        encContext_->outputBufferInfoQueue.pop();
        encContext_->outputFrameCount++;
        if (bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS) {
            OH_LOG_ERROR(LOG_APP, "Catch EOS, thread out");
            break;
        }
        lock.unlock();
        if (bufferInfo.attr.pts == ZERO) {
            bufferInfo.attr.pts = encContext_->outputFrameCount * MICROSECOND / sampleInfo_.frameRate;
        }
        OH_LOG_INFO(LOG_APP, "Out buffer count: %{public}u, size: %{public}d, flag: %{public}u, pts: %{public}ld",
                    encContext_->outputFrameCount, bufferInfo.attr.size, bufferInfo.attr.flags,
                    bufferInfo.attr.pts);
        muxer_->WriteSample(muxer_->GetVideoTrackId(), reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer),
                            bufferInfo.attr);
        int32_t ret = videoEncoder_->FreeOutputBuffer(bufferInfo.bufferIndex);
        if (ret != AVCODEC_SAMPLE_ERR_OK) break;
    }
    OH_LOG_INFO(LOG_APP, "Exit, frame count: %{public}u", encContext_->outputFrameCount);
    StartRelease();
}

void Recorder::StartRelease()
{
    if (!isReleasing_.exchange(true)) {
        OH_LOG_INFO(LOG_APP, "Start release CodecTest");
        releaseThread_ = std::make_unique<std::thread>(&Recorder::Release, this);
    }
}

void Recorder::Release()
{
    if (isExiting_.exchange(true)) {
        return;
    }
    StopAndNotifyThreads();
    ProgramExit();
}

void Recorder::StopAndNotifyThreads()
{
    isStarted_ = false;
    isReleasing_ = true;
    if (encContext_) {
        std::lock_guard<std::mutex> lock1(encContext_->inputMutex);
        std::lock_guard<std::mutex> lock2(encContext_->outputMutex);
        encContext_->inputCond.notify_all();
        encContext_->outputCond.notify_all();
    }
    {
        std::lock_guard<std::mutex> lock(videoMutex_);
        std::queue<VideoFrame> empty;
        std::swap(videoQueue_, empty);
        videoCond_.notify_all();
    }
    if (audioEncContext_) {
        std::lock_guard<std::mutex> lock1(audioEncContext_->inputMutex);
        std::lock_guard<std::mutex> lock2(audioEncContext_->outputMutex);
        audioEncContext_->inputCond.notify_all();
        audioEncContext_->outputCond.notify_all();
    }
    {
        std::lock_guard<std::mutex> lock(audioMutex_);
        audioCond_.notify_all();
    }
    {
        std::lock_guard<std::mutex> lock(videoMutex_);
        videoCond_.notify_all();
    }
    if (videoEncInputThread_ && videoEncInputThread_->joinable()) {
        videoEncInputThread_->join();
        videoEncInputThread_.reset();
    }
    if (encOutputThread_ && encOutputThread_->joinable()) {
        encOutputThread_->join();
        encOutputThread_.reset();
    }
    if (audioEncInputThread_ && audioEncInputThread_->joinable()) {
        audioEncInputThread_->join();
        audioEncInputThread_.reset();
    }
    if (audioEncOutputThread_ && audioEncOutputThread_->joinable()) {
        audioEncOutputThread_->join();
        audioEncOutputThread_.reset();
    }
}

void Recorder::ProgramExit()
{
    if (muxer_ != nullptr) {
        muxer_->Release();
        muxer_.reset();
    }
    if (videoEncoder_ != nullptr) {
        videoEncoder_->Stop();
        if (sampleInfo_.window != nullptr) {
            OH_NativeWindow_DestroyNativeWindow(sampleInfo_.window);
            sampleInfo_.window = nullptr;
        }
        videoEncoder_->Release();
        videoEncoder_.reset();
    }
    if (audioEncoder_ != nullptr) {
        audioEncoder_->Stop();
        audioEncoder_->Release();
        audioEncoder_.reset();
    }
    if (audioEncContext_ != nullptr) {
        delete audioEncContext_;
        audioEncContext_ = nullptr;
    }
    if (encContext_ != nullptr) {
        delete encContext_;
        encContext_ = nullptr;
    }
    {
        std::lock_guard<std::mutex> lock(audioMutex_);
        while (!audioQueue_.empty()) {
            audioQueue_.pop();
        }
    }
    {
        std::lock_guard<std::mutex> lock(videoMutex_);
        while (!videoQueue_.empty()) {
            videoQueue_.pop();
        }
    }
    isStarted_ = false;
    isExiting_ = false;
    isReleasing_ = false;
    isFirstFrame_ = true;
    doneCond_.notify_all();
}

int32_t Recorder::WaitForDone()
{
    OH_LOG_INFO(LOG_APP, "Wait called");
    std::unique_lock<std::mutex> lock(mutex_);
    doneCond_.wait(lock, [this]() {
        return !isReleasing_;
    });
    if (releaseThread_ && releaseThread_->joinable()) {
        releaseThread_->join();
        releaseThread_.reset();
    }
    OH_LOG_INFO(LOG_APP, "Done");
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t Recorder::Stop()
{
    if (!isStarted_) {
        return AVCODEC_SAMPLE_ERR_OK;
    }
    StartRelease();
    return WaitForDone();
}

int32_t Recorder::CreateAudioEncoder()
{
    int32_t ret = audioEncoder_->Create(sampleInfo_.audioCodecMime);
    if (ret != AVCODEC_SAMPLE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "Create audio encoder(%{public}s) failed", sampleInfo_.audioCodecMime.c_str());
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    OH_LOG_INFO(LOG_APP, "Create audio encoder(%{public}s)", sampleInfo_.audioCodecMime.c_str());

    audioEncContext_ = new CodecUserData;
    ret = audioEncoder_->Config(sampleInfo_, audioEncContext_);
    if (ret != AVCODEC_SAMPLE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "Encoder config failed");
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    return AVCODEC_SAMPLE_ERR_OK;
}

int32_t Recorder::CreateVideoEncoder()
{
    int32_t ret = videoEncoder_->Create(sampleInfo_.videoCodecMime);
    if (ret != AVCODEC_SAMPLE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "Create video encoder failed");
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    encContext_ = new CodecUserData;
    bool a = encContext_->inputBufferInfoQueue.empty();
    ret = videoEncoder_->Config(sampleInfo_, encContext_);
    if (ret != AVCODEC_SAMPLE_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, "Encoder config failed");
        return AVCODEC_SAMPLE_ERR_ERROR;
    }
    return AVCODEC_SAMPLE_ERR_OK;
}

void Recorder::AudioEncInputThread()
{
    while (isStarted_ && !isExiting_) {
        AudioFrame frame;
        {
            std::unique_lock<std::mutex> lock(audioMutex_);
            audioCond_.wait_for(lock, 5s, [this]() {
                return !isStarted_ || isExiting_ || !audioQueue_.empty();
            });
            if (!isStarted_ || isExiting_) break;
            if (audioQueue_.empty()) continue;
            frame = std::move(audioQueue_.front());
            audioQueue_.pop();
        }
        std::unique_lock<std::mutex> lock(audioEncContext_->inputMutex);
        audioEncContext_->inputCond.wait_for(lock, 5s, [this]() {
            return !isStarted_ || isExiting_ || !audioEncContext_->inputBufferInfoQueue.empty();
        });
        if (!isStarted_ || isExiting_) break;
        if (audioEncContext_->inputBufferInfoQueue.empty()) continue;
        CodecBufferInfo bufferInfo = audioEncContext_->inputBufferInfoQueue.front();
        audioEncContext_->inputBufferInfoQueue.pop();
        audioEncContext_->inputFrameCount++;
        if (!bufferInfo.buffer) {
            OH_LOG_ERROR(LOG_APP, "Invalid audio buffer");
            continue;
        }
        uint8_t *inputBufferAddr = OH_AVBuffer_GetAddr(reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer));
        if (!inputBufferAddr) {
            OH_LOG_ERROR(LOG_APP, "Failed to get audio buffer address");
            continue;
        }
        std::copy(frame.data.begin(), frame.data.end(), inputBufferAddr);
        bufferInfo.attr.size = frame.data.size();
        bufferInfo.attr.pts = frame.timestamp;
        if (isFirstFrame_) {
            bufferInfo.attr.flags = AVCODEC_BUFFER_FLAGS_CODEC_DATA;
            isFirstFrame_ = false;
        } else {
            bufferInfo.attr.flags = AVCODEC_BUFFER_FLAGS_NONE;
        }
        lock.unlock();
        int32_t ret = audioEncoder_->PushInputData(bufferInfo);
        if (ret != AVCODEC_SAMPLE_ERR_OK) {
            OH_LOG_ERROR(LOG_APP, "Push data failed, thread out");
            break;
        }
    }
}

void Recorder::AudioEncOutputThread()
{
    while (isStarted_ && !isExiting_) {
        std::unique_lock<std::mutex> lock(audioEncContext_->outputMutex);
        audioEncContext_->outputCond.wait_for(lock, 5s, [this]() {
            return !isStarted_ || isExiting_ || !audioEncContext_->outputBufferInfoQueue.empty();
        });
        if (!isStarted_ || isExiting_) break;
        if (audioEncContext_->outputBufferInfoQueue.empty()) continue;
        CodecBufferInfo bufferInfo = audioEncContext_->outputBufferInfoQueue.front();
        audioEncContext_->outputBufferInfoQueue.pop();
        audioEncContext_->outputFrameCount++;
        if (bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS) {
            OH_LOG_INFO(LOG_APP, "Received video EOS");
            continue;
        }
        if (!bufferInfo.buffer) {
            OH_LOG_ERROR(LOG_APP, "Invalid audio output buffer");
            continue;
        }
        OH_LOG_INFO(LOG_APP,
            "Audio Out buffer count: %{public}u, size: %{public}d, flag: %{public}u, pts: %{public}ld",
            audioEncContext_->outputFrameCount, bufferInfo.attr.size, bufferInfo.attr.flags, bufferInfo.attr.pts);
        muxer_->WriteSample(muxer_->GetAudioTrackId(), reinterpret_cast<OH_AVBuffer*>(bufferInfo.buffer),
                            bufferInfo.attr);
        int32_t ret = audioEncoder_->FreeOutputData(bufferInfo.bufferIndex);
        if (ret != AVCODEC_SAMPLE_ERR_OK) break;
    }
    OH_LOG_INFO(LOG_APP, "Exit, frame count: %{public}u", audioEncContext_->inputFrameCount);
    StartRelease();
}